class WxRequest {
  // 定义实例属性，用来设置默认参数
  defaults = {
    baseURL: '',
    url: '',
    data: null,
    method: 'GET',
    header: {
      'Content-type': 'application/json'
    },
    timeout: 60000,
    isLoading: true //控制是否使用默认loading
  }

  // 定义拦截器对象
  interceptors = {
    // 请求拦截器
    request: (config) => config,
    // 响应拦截器
    response: (response) => response
  }

  // 定义数组队列
  // 初始值为空数组 用来存储请求标识
  queue = []

  // 创建和初始化类的属性以及方法
  // 实例化传入的参数会被constructor形参进行接收
  constructor(params = {}) {
    // 合并请求参数 传入参数放在最后覆盖默认参数
    this.defaults = Object.assign({}, this.defaults, params)
  }

  request(options) {
    // 如果有新请求 就清除上一次的定时器
    this.timerId && clearTimeout(this.timerId)
    // 需要先合并完整的请求地址
    options.url = this.defaults.baseURL + options.url
    // 合并请求参数
    options = { ...this.defaults, ...options }
    // 在请求发送之前，添加loading效果
    // wx.showLoading()

    if (options.isLoading && options.method !== 'UPLOAD') {
      // 判断队列queue是否为空 为空显示loading
      this.queue.length === 0 && wx.showLoading()

      // 向队列中添加请求标识
      // 每个标识代表一个请求
      this.queue.push('request')
    }

    // 在请求发送之前，调用请求拦截器，新增和修改参数
    options = this.interceptors.request(options)
    // 需要使用 Promise 封装 wx.request,处理异步请求
    return new Promise((resolve, reject) => {
      if (options.method === 'UPLOAD') {
        wx.uploadFile({
          ...options,
          success: (res) => {
            // 需要将服务器返回的JSON字符串通过JSON.parse转为对象
            res.data = JSON.parse(res.data)
            // 合并参数
            const mergeRes = Object.assign({}, res, {
              config: options,
              isSuccess: true
            })
            resolve(this.interceptors.response(mergeRes))
          },
          fail: (err) => {
            // 合并参数
            const mergeErr = Object.assign({}, err, {
              config: options,
              isSuccess: false
            })
            resolve(this.interceptors.reject(mergeErr))
          }
        })
      } else {
        wx.request({
          ...options,

          // 接口调用成功时会调用
          //  1. 如果请求成功，将响应成功的数据传递给响应拦截器，同时在传递的数据中新增 `isSuccess: true` 字段，表示请求成功
          // 2. 如果请求失败，将响应失败的数据传递给响应拦截器，同时在传递的数据中新增 `isSuccess: false` 字段，表示请求失败
          success: (res) => {
            // 响应拦截器调用 并合并参数
            const mergeRes = Object.assign({}, res, {
              config: options,
              isSuccess: true
            })
            resolve(this.interceptors.response(mergeRes))
          },
          fail: (err) => {
            // 响应拦截器调用 并合并参数
            const mergeErr = Object.assign({}, err, {
              config: options,
              isSuccess: false
            })
            reject(this.interceptors.response(mergeErr))
          },
          complete: () => {
            if (options.isLoading) {
              // 在每一次请求结束以后 都移除一个请求标识
              this.queue.pop()
              // 在删除标识以后 需要判断queue数组是否为空
              // 如果为空 说明并发请求完成 则隐藏loading效果
              this.queue.length === 0 && this.queue.push('request')

              this.timerId = setTimeout(() => {
                this.queue.pop()
                this.queue.length === 0 && wx.hideLoading()
                clearTimeout(this.timerId)
              }, 1)
            }
          }
        })
      }
    })
  }

  get(url, data = {}, config = {}) {
    return this.request(Object.assign({ url, data, method: 'GET' }, config))
  }

  post(url, data = {}, config = {}) {
    return this.request(Object.assign({ url, data, method: 'POST' }, config))
  }

  delete(url, data = {}, config = {}) {
    return this.request(Object.assign({ url, data, method: 'DELETE' }, config))
  }

  put(url, data = {}, config = {}) {
    return this.request(Object.assign({ url, data, method: 'PUT' }, config))
  }

  // 处理并发请求
  all(...promise) {
    // 通过展开运算符接收传递的参数
    // 展开运算符会将传入参数转为数组
    return Promise.all(promise)
  }

  /**
   * @description upload实例方法 用来封装 wx.uploadFile进行封装
   * @param {*} url 文件的上传地址、接口地址
   * @param {*} filePath  要上传的文件资源路径
   * @param {*} name  文件对应的key
   * @param {*} config  其他配置项
   */
  upload(url, filePath, name = 'file', config = {}) {
    return this.request(
      Object.assign({ url, filePath, name, method: 'OPLOAD' }, config)
    )
  }
}

export default WxRequest
